#include <iostream>
#include <memory>

// 设计分数类
class Rational
{
private:
    int num_; // 分子
    int den_; // 分母

    void reduce()
    {
        if (den_ < 0)
        {
            num_ = -num_;
            den_ = -den_;
        }
        int gcd = computeGCD(std::abs(num_), den_);
        num_ /= gcd;
        den_ /= gcd;
    }

    static int computeGCD(int a, int b)
    {
        while (b != 0)
        {
            int temp = b;
            b = a % b;
            a = temp;
        }
        return a;
    }

public:
    // 1.构造函数如何约束对象创建？
    explicit Rational(int numerator = 0, int denomiator = 1)
    {
        if (denomiator == 0)
        {
            throw std::invalid_argument("Denomiatior cannot be zero");
        }

        // 保证分母为正
        if (denomiator < 0)
        {
            numerator = -numerator;
            denomiator = -denomiator;
        }

        // 约分
        int gcd = computeGCD(std::abs(numerator), denomiator);
        num_ = numerator / gcd;
        den_ = denomiator / gcd;
    }

    // 2.拷贝/移动语义如何？
    Rational(const Rational &other) = default;            // 允许拷贝
    Rational &operator=(const Rational &other) = default; // 允许赋值

    // 移动语义
    Rational(Rational &&other) noexcept = default;
    Rational &operator=(Rational &&other) noexcept = default;

    // 3.对象销毁时会发生什么？
    ~Rational() = default; // 隐式析构足够

    // 4.对象的初始化和赋值有什么区别？
    // 初始化：使用构造函数
    // 赋值：使用赋值运算符

    // 5.对象的按值传递意味着什么？
    // 通过拷贝构造函数复制

    // 6.什么是对象的合法值
    // 分母必须为正，分数需要简化

    // 7.继承的对象是什么？
    // 这个类不需要继承 设置为final

    // 8.需要什么类型转换？
    explicit operator double() const
    { // 显式类型转换
        return static_cast<double>(num_) / den_;
    }

    // 9.需要什么运算符和函数？
    Rational &operator+=(const Rational &rhs)
    {
        num_ = num_ * rhs.den_ + rhs.num_ * den_;
        den_ *= rhs.den_;
        reduce();
        return *this;
    }

    // 10.有那些函数应该是private的？
    // 11.谁该取用成员？
    int numerator() const { return num_; }
    int denomerator() const { return den_; }

    // 12. 什么是未申明接口？
    // 包括性能保证、异常保证等

    // 13. 这各类有多么一般化？
    // 可以考虑模板化支持不同整数类型
};

// 14.非成员函数的设计
// 实现算术运算符
inline Rational operator+(Rational lhs, const Rational &rhs)
{
    return lhs + rhs;
}

inline bool operator==(const Rational &lhs, const Rational &rhs)
{
    return lhs.numerator() == rhs.numerator() && lhs.denomerator() == rhs.denomerator();
}

// 15. 输入输出操作符
std::ostream &operator<<(std::ostream &os, const Rational &r)
{
    return os << r.numerator() << "/" << r.denomerator();
}

// 使用示例
void usageExample()
{
    // 构造和初始化
    Rational r1(3, 4); // 3/4
    Rational r2(6, 8); // 自动简化为 3/4

    // 显式构造防止意外转换
    // Rational r3 = 2;    // 错误：需要显式构造
    Rational r3(2); // 正确：显式构造

    // 算术运算
    Rational sum = r1 + r2; // 运算符重载

    // 类型转换
    double d = static_cast<double>(r1); // 显式转换

    // 输出
    std::cout << r1 << " + " << r2 << " = " << sum << std::endl;

    // 异常处理
    try
    {
        Rational invalid(1, 0); // 抛出异常
    }
    catch (const std::invalid_argument &e)
    {
        std::cerr << "Error: " << e.what() << std::endl;
    }
}

// 进一步的泛化：模板版本
template <typename T>
class RationalT
{
public:
    explicit RationalT(T n = 0, T d = 1) : num_(n), den_(d)
    {
        static_assert(std::is_integral<T>::value,
                      "RationalT requires integral type");
        if (d == 0)
            throw std::invalid_argument("Denominator cannot be zero");
        reduce();
    }
    // ... 其他成员函数类似
private:
    T num_;
    T den_;
};